package jmine.tec.web.front.workflow;

import static jmine.tec.web.front.domain.BackOfficeRequestRevisionOutcome.AUTHORIZED;
import static jmine.tec.web.front.domain.BackOfficeRequestRevisionOutcome.REJECTED;
import jmine.tec.web.front.FrontException;
import jmine.tec.web.front.FrontMessages;
import jmine.tec.web.front.dao.BackOfficeRequestRevisionDAO;
import jmine.tec.web.front.dao.BackOfficeRequestStatusDAO;
import jmine.tec.web.front.dao.BackOfficeRequestStatusSystemIdentifier;
import jmine.tec.web.front.dispatcher.request.BackOfficeRequestLifecycle;
import jmine.tec.web.front.dispatcher.request.BackOfficeRequestSender;
import jmine.tec.web.front.domain.BackOfficeRequest;
import jmine.tec.web.front.domain.BackOfficeRequestRevision;
import jmine.tec.web.front.domain.BackOfficeRequestRevisionOutcome;
import jmine.tec.web.front.domain.BackOfficeRequestStatus;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.persist.persister.StatelessPersister;

/**
 * A lifecycle that is represented by a {@link BackOfficeRequestStatusWorkflow}, it sends the request on the last successful state.
 * 
 * @author wagner.tsuchiya
 */
public class BackOfficeRequestWorkflowLifecyle implements BackOfficeRequestLifecycle {
    private final BackOfficeRequestStatusDAO backOfficeRequestStatusDAO;

    private final BackOfficeRequestRevisionDAO backOfficeRequestRevisionDAO;

    private final StatelessPersister persister;

    private final BackOfficeRequestSender sender;

    private final BackOfficeRequestStatusWorkflow workflow;

    private final Class<? extends BackOfficeRequestStatusSystemIdentifier> systemIdentifier;

    /**
     * Constructor
     * 
     * @param backOfficeRequestStatusDAO backOfficeRequestStatusDAO
     * @param backOfficeRequestRevisionDAO backOfficeRequestRevisionDAO
     * @param persister persister
     * @param sender sender
     * @param workflow the workflow
     * @param systemIdentifier the system identifier type, for this workflow.
     */
    public BackOfficeRequestWorkflowLifecyle(BackOfficeRequestStatusDAO backOfficeRequestStatusDAO,
            BackOfficeRequestRevisionDAO backOfficeRequestRevisionDAO, StatelessPersister persister, BackOfficeRequestSender sender,
            BackOfficeRequestStatusWorkflow workflow, Class<? extends BackOfficeRequestStatusSystemIdentifier> systemIdentifier) {
        super();
        this.backOfficeRequestStatusDAO = backOfficeRequestStatusDAO;
        this.backOfficeRequestRevisionDAO = backOfficeRequestRevisionDAO;
        this.persister = persister;
        this.sender = sender;
        this.workflow = workflow;
        this.systemIdentifier = systemIdentifier;
    }

    /**
     * {@inheritDoc}
     */
    public void process(BackOfficeRequest request) {
        BackOfficeRequestStatusSystemIdentifier currentSystemId = this.getCurrentSystemIdentifier(request.getStatus());
        BackOfficeRequestStatusSystemIdentifier nextSystemId;
        if (!this.workflow.isLeafVertex(currentSystemId)) {
            BackOfficeRequestRevision lastRevision;
            try {
                lastRevision = this.getLastRevision(request);
            } catch (BeanNotFoundException e) {
                throw new FrontException(FrontMessages.REVISION_NOT_FOUND.create());
            }
            if (lastRevision.getOutcome().equals(REJECTED)) {
                nextSystemId = this.workflow.getOnRejection(currentSystemId);
            } else if (lastRevision.getOutcome().equals(AUTHORIZED)) {
                nextSystemId = this.workflow.getOnAuthorization(currentSystemId);
            } else if (lastRevision.getOutcome().equals(BackOfficeRequestRevisionOutcome.NEUTRAL)) {
                // Nothing happens.
                nextSystemId = currentSystemId;
            } else {
                throw new FrontException(FrontMessages.REVISION_OUTCOME_NOT_SUPPORTED.create());
            }
            request.setStatus(this.backOfficeRequestStatusDAO.findBySystemIdentifier(nextSystemId));
            // Se a última revisão foi de autorização e é o último vértice.
            if (lastRevision.getOutcome().equals(AUTHORIZED) && this.workflow.isLeafVertex(nextSystemId)) {
                this.sender.send(request);
            }
            this.persister.save(request);
        }
    }

    /**
     * @param status find the current system id.
     * @return the system id.
     */
    private BackOfficeRequestStatusSystemIdentifier getCurrentSystemIdentifier(BackOfficeRequestStatus status) {
        for (BackOfficeRequestStatusSystemIdentifier systemId : this.systemIdentifier.getEnumConstants()) {
            if (status.isIdentifiedBy(systemId)) {
                return systemId;
            }
        }
        throw new FrontException(FrontMessages.SYSTEM_IDENTIFIER_NOT_FOUND_FOR.create(status.getName()));
    }

    /**
     * Returns the last revision.
     * 
     * @param request request
     * @return boolean
     * @throws BeanNotFoundException exception
     */
    private BackOfficeRequestRevision getLastRevision(BackOfficeRequest request) throws BeanNotFoundException {
        return this.backOfficeRequestRevisionDAO.findLastByRequest(request);
    }
}
